Previous Book Contents Book Index Next

Inside Macintosh: Open Transport /
Chapter 5 - Option Management


Using Options

This section describes the rules for option negotiation and how negotiation is affected by the function you use to set options. It also explains how you use endpoint functions to set and retrieve option values and how you use Open Transport utility functions to construct an options buffer and parse through an options buffer.

If your application needs to negotiate option values, you must read the sections "Determining Which Function to Use to Negotiate Options," "Negotiating Options," and "Obtaining the Maximum Size of an Options Buffer." After reading these sections, you can read whichever of the remaining sections describes the task you need to accomplish.

Determining Which Function to Use to Negotiate Options

You can negotiate options using the OTOptionManagement function or using any one of the endpoint functions used to transfer data or establish a connection. The following bulleted list summarizes the major differences between using
the OTOptionManagement function or using other endpoint functions to set an option value.

Negotiating Options

This section describes the rules governing option negotiation and the error conditions that might occur during this process. Unless stated otherwise, these rules apply to all functions that allow you to specify option values.

A basic rule to keep in mind is that options change only as the result of successful negotiations or partly successful negotiations. If you use any function except the OTOptionManagement function, the changes last for the duration of that function invocation. Option values are not changed by a change in the state of an endpoint. Once you change an option value permanently, there is no function that you can call to restore an option to its previous value, unless that previous value is the default value.

Negotiating Multiple Options

You can use one function to negotiate several options by placing the options
in the options buffer passed to the function. If one of the options is ignored
or rejected for any reason, the outcome depends on the function you use to
set options.

If you specify the same option more than once, the endpoint provider does not check for duplicate occurrences of the same option. It simply processes the options one after another. However, the endpoint provider might negotiate options in any order; therefore, it is not safe to make any assumptions that a later occurrence of an option will override an earlier occurrence.

Initiating an Option Negotiation

You initiate an option negotiation by calling the OTOptionManagement function with the flag T_NEGOTIATE set or by calling the OTConnect, OTSndUData, or OTSndURequest function and specifying an options buffer length that is greater than 0. You can specify values for some or all of the options supported by an endpoint. The endpoint provider takes values for options that you do not specify explicitly in the options buffer, from the endpoint's internal options buffer. This buffer contains the endpoint's current option values; these could be default values, values that you specified when you configured the provider, or values resulting from a previous negotiation.

If the endpoint supports an option, the possible outcome of option negotiation depends on whether the option is an absolute requirement, as described in the next two sections. If the endpoint does not support the option, the OTOptionManagement function reports T_NOTSUPPORT in the status field. The OTConnect, OTSndUData, or OTSndURequest functions ignore the option.

Options That Are Absolute Requirements

If the option is an absolute requirement, the result of the negotiation depends on whether the negotiated value is the same as the requested value. If it is, the status field in the TOption structure describing the option is set to T_SUCCESS when the function returns. If the negotiated value is not the same as the requested value, the result depends on the function used to negotiate
the option:

Options That Are not Absolute Requirements

If the requested option is not an absolute requirement, the result of the negotiation depends on whether the negotiated value is the same as the requested value. If it is, the endpoint provider sets the status field of the TOption structure describing the option to T_SUCCESS. If the negotiated value is different than the proposed value, the endpoint provider sets the status field of the TOption structure describing the options to T_PARTSUCCESS.

Conflicting Option Values

It is possible that a requested option value conflicts with the value of another option that is proposed with the same call to the function or that is currently effective. The endpoint provider might not detect these conflicts immediately, and later they might lead to unpredictable results. If the endpoint provider detects conflicts at negotiation time, the conflicts are resolved according to the rules stated above.

An endpoint provider usually detects conflicts at the time it establishes a connection or sends a datagram. Consequently, if you use the OTOptionManagement function to set options, you might not become aware
that there is a problem due to conflicting options until the options are
actually exercised during connection establishment or data transmission.

Privileged or Read-Only Options

A protocol implementation can define options to be privileged or read only. These two categories are not necessarily separate. A privileged option might be inaccessible or read-only for nonprivileged clients. An option might be read-only for all clients or solely for nonprivileged clients. Here are two general guidelines to keep in mind:

If you request negotiation of a privileged option using the OTOptionManagement function, the function returns successfully with the status field of the privileged option set to T_NOTSUPPORT. If you use the OTConnect, OTAccept, OTSndUData, or OTSndURequest functions, the option is ignored--that is, the function result is not affected by the fact that the options are not supported.

If you request negotiation of a read-only option using the OTOptionManagement function, the function returns with the status field of the read-only option set to T_READONLY. If you use any other function to change a read-only option, the results vary with the function used:

Error Conditions

Option negotiation might be affected if you try to negotiate an illegal option, a privileged or read-only option, an unsupported option, or an option for an unsupported protocol (level). The results of attempting to negotiate privileged or read-only options are described in "Privileged or Read-Only Options" on page 5-15. This section explains the outcome of negotiating illegal options and describes other problems that might arise during option negotiation.

An option is illegal in these cases:

If you specify an illegal option, the following error conditions result depending on the function you used:

If the options buffer you pass to a function contains multiple options and one of them is illegal, the function fails as described. However, if you used the OTOptionManagement function to set options, it is possible that some or all of the legal options in the buffer were successfully negotiated. You can check the current status for the endpoint by calling the OTOptionManagement function with the T_CURRENT flag set.

The OTOptionManagement function fails with the kOTBadOptionErr result if you specify an unknown value for the option protocol level. Using any other function to specify an unknown option level does not cause the function to fail, but results in the option being ignored.

Specifying an option name that is unknown or unsupported by the endpoint does not cause a function to fail. The OTOptionManagement function returns T_NOTSUPPORT in the status field for the option; the other endpoint functions ignore the unknown options.

Obtaining the Maximum Size of an Options Buffer

Different types of endpoints support different numbers of options. For example, an ATP endpoint might support more options than a DDP endpoint and might need a larger buffer to hold the options. When you call the OTOptionManagement function to change option values, the function returns in the ret parameter a pointer to the buffer containing the negotiated option values. You must have allocated the buffer used to store these options before calling the function. Likewise, when you call the OTListen, OTRcvUData, OTRcvURequest or OTRcvConnect functions, you can allocate a buffer in which current option values are to be placed when these functions return. In either case, you must specify the size of the buffer, and the buffer must be large enough to hold all of the endpoint's options. Otherwise, the function fails with a kOTBufferOverflow result. You can obtain the maximum size of a buffer used to store options for your endpoint by examining the options field of the TEndpointInfo structure for the endpoint. You can get a pointer to this structure when you open the endpoint, when you bind the endpoint, or when you call the OTGetEndpointInfo function.

Setting Option Values

You can use the OTOptionManagement, OTAccept, OTSndUData, OTSndURequest, and OTConnect functions to set option values. Setting option values results in a negotiation process between you (the client application) and the endpoint provider or, in the case of association-related options, between local and remote clients and their endpoint providers. The section "Initiating an Option Negotiation" on page 5-13 describes the rules that govern an option negotiation that you have initiated using the OTOptionManagement, OTConnect, OTSndUData, or OTSndURequest functions. The section "Retrieving Values for Connection-Oriented Endpoints," beginning on page 5-21 describes the negotiation rules that hold when you use the OTOptionManagement or OTAccept functions to respond to a negotiation. This section describes ways in which you can build the options buffer used to specify the options you want to change.

Specifying Option Values

No matter which function you use to set option values, you must allocate a buffer that contains the option value or values you want to change. The options in this buffer are described by TOption structures; the format of this structure is illustrated in Figure 5-2 on page 5-8. You can concatenate several structures in the buffer, as shown by Figure 5-3 on page 5-8, so long as each structure begins on a long-word boundary. The buffer itself is described by a TNetbuf structure that specifies the location of the buffer and its size.

You can create a buffer that contains the option values you want to set in one of two ways: manually or by using the OTCreateOptions function. If you construct the buffer manually, you must do the following:

  1. Allocate the buffer.
  2. Create a TOption structure for each option you want to change.
  3. Initialize each field of the TOption structure except for the status field.
  4. Place the TOption structures in the buffer, making sure that each begins on a long-word boundary. This enables Open Transport to parse the buffer.
  5. Append a null character to the end of the buffer. This enables Open Transport to tell that it has reached the end of the buffer.

To have Open Transport create a buffer for you, you must call the OTCreateOptions function and pass it a string containing one or more option values. This method saves time and trouble, but you can only use it if all the options in the buffer are for the same level and that level is the same as the top-level protocol for the endpoint provider. That is to say, you could not use this method to construct a buffer that contains DDP-level options for an ATP endpoint. In addition, this method is only guaranteed to work if you are building an options buffer for the OTOptionManagement function.

Listing 5-1 shows how you construct an options buffer manually. The listing creates and initializes two TOption structures, ddpOpt and atpOpt. It allocates a buffer large enough to contain the TOption structures and then places those structures in the buffer. Note that the structures are quad-word aligned and that a null character is appended to the end of the buffer.

Listing 5-1 Constructing an options buffer manually

TOption *ddpOpt, *atpOpt;
unsigned char optionBuffer[41];

ddpOpt = (TOption*)&optionBuffer[0];
ddpOpt->len = 20;                
ddpOpt->level = ATK_DDP;
ddpOpt->name = OPT_CHECKSUM;
ddpOpt->status = 0;
ddpOpt->value[0] = 1;            /* turn checksumming on */

atpOpt = (TOption*)&optionBuffer[20]
atpOpt->len = 20;                
atpOpt->level = ATK_ATP;
atpOpt->name = OPT_RELTIMER;
atpOpt->status = 0;
atpOpt->value[0] = 2; /* purge transaction list every 2 minutes */

optionBuffer[40] = 0;/* add null character to end of buffer */
Listing 5-2 shows how you construct an options buffer by using the OTCreateOptions function. The code initializes a string array, myStr, to hold option values. It then creates a TOptMgmt structure, which would later be passed to the OTOptionManagement function to request the option values specified in the string. Finally, it calls the OTCreateOptions function to create the options buffer. The OTCreateOptions function creates the TOption structures and places them in the buffer, making sure that the structures are properly aligned.

Listing 5-2 Constructing an options buffer using the OTCreateOptions function

char* myStr = "BaudRate = 9650 DataBits = 8 Parity = 0 
                     StopBits = 10";
UInt8 buffer[512];
TOptMgmt cmd;
cmd.opt.len = 0;
cmd.opt.maxlen = sizeof(buffer);
cmd.opt.buf = buffer;
cmd.flags = T_NEGOTIATE
err = OTCreateOptions("SerialA", &myStr, &cmd.opt)
In this case, the initial value of cmd.opt.len, which is 0, tells the OTCreateOptions function at what offset it should begin to append option information in the buffer. When the function returns, this field specifies the actual length of the buffer.

Setting Default Values

To set all of an endpoint's options to their default values, call the OTOptionManagement function, specifying T_NEGOTIATE for the flags field and allocating a buffer containing only one option named T_ALLOPT. Doing this saves you the trouble of constructing a TOption structure for every option the endpoint supports. However, there is no guarantee that the provider can honor your request simply because you request default values. Therefore, you must allocate a buffer that is large enough to hold the option values returned in the ret parameter.

Allowing the Endpoint Provider to Select an Option Value

You can specify that an endpoint provider selects an appropriate option value by setting the endpoint's value field to the constant T_UNSPEC. This is especially useful in complex options such as ISO throughput where the option value has an internal structure.

Retrieving Option Values

This section describes how you can retrieve information about options, including obtaining current and default option values for an endpoint and obtaining current option values related to a connection, transaction, or datagram.

When retrieving option values, you must allocate a buffer that is large enough to contain the options when the function returns. The section "Obtaining the Maximum Size of an Options Buffer" on page 5-17 explains how you do this.

Obtaining Current and Default Values

To obtain some of an endpoint's default or current option values, you call the OTOptionManagement function. You specify T_DEFAULT or T_CURRENT for the flags field of the req parameter, and you use the option.buf field to specify the option names in which you are interested. When the function returns, it places TOption structures, describing the default or current option values, in the buffer referenced by the opt.buf field of the ret parameter.

If you are interested in obtaining all of an endpoint's default or current values, you can use the following methods:

Using T_ALLOPT for the option name allows you to construct an input buffer that contains only one option. Remember, however, that you must allocate an output buffer that is large enough to hold all of an endpoint's option values when the function returns.

Retrieving Values for Connection-Oriented Endpoints

When you are establishing a connection, it is possible to negotiate association-
related option values at every point in the connection process, as illustrated in Figure 5-1 on page 5-6. Both the active and passive peers might want to retrieve option values during this process.

The passive peer might want to know the proposed option values under negotiation. It can retrieve these by calling the OTListen function. After examining the option values returned by the OTListen function, the passive peer can negotiate option values by specifying the desired option values
with the OTAccept call used to accept the connection. Using this method,
the passive peer can examine the requested option values before proposing alternate values.

The passive peer can also negotiate alternate values by using the OTOptionManagement function to preset option values for the endpoint accepting the connection. This sets the current option values for the endpoint so that when the passive peer calls the OTAccept function, these are the option values that are negotiated with the requested values.

The passive peer can try to negotiate option values that are higher than the proposed values. The outcome depends on the protocol. If the protocol rejects the new option values, the connection fails, and the endpoint provider issues a T_DISCONNECT event. Depending on timing and the implementation, the OTAccept function either succeeds or fails with the kOTLookErr result.

The association-related options retrieved by the passive peer are related to the incoming connection, identified by a sequence number, and are not related to the listening endpoint. Option values currently effective for the listening endpoint might affect the values retrieved by the OTListen function because the endpoint is involved in the negotiation process, but these values are not the same as the option values related to the connection request. That is to say, calling the OTOptionManagement function to retrieve the option values that were currently effective for the listening endpoint is likely to yield a different set of values than you would find by examining the values of options passed in the call parameter to the OTListen function.

When you establish the connection--that is, when a synchronous call to the OTConnect function returns or when the active peer calls the OTRcvConnect function-- all final negotiated values effective for the connection are returned in the buffer passed in the rcvCall or call parameter, respectively. These option values include all association-related options that were received with the connection response and the negotiated values of those non-association-
related options that had been specified on input. Options specified on input to the OTConnect call that are not supported or that refer to an unknown protocol are ignored and not returned by the OTConnect or OTRcvConnect function when it returns.

Retrieving Values for Connectionless Transactionless Endpoints

You can retrieve association-related options set for connectionless transactionless endpoints by examining the buffer passed in the udata parameter to the OTRcvUData function. These options relate to the incoming datagram, not to the endpoint receiving it. For example, the IEEE 802.2 protocol uses option values to specify whether a datagram is a multicast or broadcast packet.

Because the options you retrieve are related to the datagram and not to the listening endpoint, their number and values can change with every transmission.

Because you are receiving information--that is, you are simply reading the contents of the options buffer--you can ignore the status field for these options.

Retrieving Values for Connectionless Transaction-Based Endpoints

You can retrieve association-related options set for connectionless transaction-
based endpoints by examining the buffer passed in the req parameter to the OTRcvURequest function. These options relate to the current transaction, not to the endpoint receiving the request. Consequently, options and their values can change with each transaction.

Because you are receiving information--that is, you are simply reading
the contents of the options buffer--you can ignore the status field for
these options.

Parsing an Options Buffer

If you use the OTOptionManagement function to set, verify, or retrieve values, the function returns in the ret parameter a pointer to a buffer containing option information. You can use the OTCreateOptionString function to parse this buffer and create a string that lists all options and their current values.

The code fragment shown in Listing 5-3 calls the OTOptionManagement function to retrieve the option values currently effective for an endpoint. On return, the OTOptionManagement function stores these in the cmd structure. Next, the code calls the OTCreateOptionString function. The first input parameter, "SerialA", specifies the name of the protocol. The next input parameter, opts, is a pointer to the buffer containing the option values returned by the OTOptionManagement function. The expression cmd.opt.buf + cmd.opt.len, which provides the next input parameter, specifies the length of the buffer. Using this information, the OTCreateOptionString function returns a string containing each option name and its respective value. The final parameter to the OTCreateOptionString function specifies the length of the string.

Listing 5-3 Using the OTCreateOptionString function to parse through a buffer

TOptMgmt    cmd;
UINt8       myBuffer[512];
char        myString[256]

cmd.opt.len = sizeof(TOption);
cmd.opt.maxlen = sizeof(myBuffer);
cmd.opt.buf = myBuffer;
((TOption*) buffer)->len = sizeof(TOption);
((TOption*) buffer)->level = COM_SERIAL;
((TOption*) buffer)->name = T_ALLOPT;
((TOption*) buffer)->status = 0;
cmd.flags = T_CURRENT;

OTOptionManagement(theEndpt, &cmd, &cmd);

TOption* opts = (TOption*)cmd.opt.buf;
err = OTCreateOptionString("SerialA", &opts, 
      cmd.opt.buf + cmd.opt.len, string, sizeof(string));
printf("Options = \"%s\"", string);
Note
The OTCreateOptionString function is supplied solely as a debugging aid. You should not include the function in a production version of your application because there is no provision made for localizing string information.

Verifying Option Values

In addition to obtaining default or current values and negotiating new values, you can use the OTOptionManagement function to verify whether an endpoint supports one or more options. To do this, you construct a buffer containing TOption structures describing the options you are interested in and pass this buffer in the req parameter to the OTOptionManagement function, specifying T_CHECK for the action flag. When the function returns, you can examine the status field of the TOption structures for the options passed back to you in the ret parameter to determine whether the specified options are supported.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
15 AUG 1996